package start.spring.basic.dynamic;

import java.security.SecureRandom;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import start.spring.basic.util.common.StringUtil;

/**
 * 
 * ****************************************************************************
 * 分布式redis锁工具类
 *
 * @author(作者)：xuyongyun	
 * @date(创建日期)：2021年8月5日
 ******************************************************************************
 */
@Repository
public class RedLockUtil {
	
	@Autowired
	DynamicRedisDAO redis;
	
	/**
	 * 获取可自动释放的redis锁
	 *	
	 * @param redisId 执行加锁操作的redis的id
	 * 
	 * @param key 需要加锁的key
	 * @param expire 锁的超时时间
	 * 
	 * @return
	 * @since           1.0
	 */
	public boolean lock(String redisId,String key,int expire){
		
		boolean result = false;
		
		//是否需要执行加锁操作
		boolean isNeedLock = false;
		
		//检测key是否存在并且成功设置了超时时间
		long keyExist = redis.ttl(redisId,key);
		if(keyExist>=0){
			//key存在并且已设置超时时间
			result = false;
			isNeedLock = false;
		}else if(keyExist==-1){
			//key存在，但是上次设置超时时间出错
			result = false;
			isNeedLock = false;
			
			//重新对key设置超时时间，保证key在下一次调用时已经被解锁
			redis.expire(redisId, key, expire);
		}else if(keyExist==-2){
			//key不存在，可以上锁
			isNeedLock = true;
		}
		
		//随机延时1到20毫秒
		SecureRandom rand = new SecureRandom();
		int sleepTime = rand.nextInt(20) + 1;
		try {
			Thread.sleep(sleepTime);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
			Thread.currentThread().interrupt();
		}
		
		if(isNeedLock){
			//需要上锁
			if(redis.setnx(redisId, key, "1")){
				//上锁成功
				try{
					//设置锁的超时时间
					redis.expire(redisId, key, expire);
				}catch(Exception e){
					//出现异常认为加锁失败
					result = false;
					e.printStackTrace();
				}
				result = true;
			}else{//上锁失败
				result = false;
			}
		}
		return result;
	}
	
	/**
	 * 加锁方法，此方法用于需要根据锁定值判断是否需要枷锁的方法
	 * 此方法获取到锁后，永久生效，解锁需要主动释放
	 *	
	 * @param key 锁定内容的唯一标识
	 * @param value 锁中的值
	 * 
	 * @return true 获取到锁，false未获取到
	 * 
	 * @since           1.0
	 */
	public boolean lock(String redisId, String key,String value){
		
		boolean result = false;
		
		//需要上锁
		if(redis.setnx(redisId, key, value)){
			result = true;
		}else{//上锁失败
			result = false;
		}
		
		return result;
	}
	
	/**
	 * 此方法用于手工解锁，或者业务操作完成之后主动解锁
	 *	
	 * @param key 需要解锁的key
	 * @return
	 * @since           1.0
	 */
	public boolean unlock(String redisId,String key){
		boolean result = true;
		if(StringUtil.isEmpty(key)){
			//key为空
			result = false;
		}else{
			redis.expire(redisId, key, 1);
		}
		return result;
	}
}
